package qc.module.platform.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import qc.common.core.unify.QCUnifyReturnValue;
import qc.common.core.utils.DateUtil;
import qc.module.platform.dto.blacklist.BlackListAddDto;
import qc.module.platform.dto.blacklist.BlackListDto;
import qc.module.platform.dto.blacklist.BlackListQueryConditionDto;
import qc.module.platform.entity.QcBlackList;
import qc.module.platform.mapper.BlackListMapper;
import qc.module.platform.repository.QcBlackListRepository;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * IpBlackListService
 *
 * @author QuCheng Tech
 * @since 2025/1/10
 */
@Service
public class BlackListService {
    private QcBlackListRepository repository;

    @Autowired
    public void setQcIpBlackListRepository(QcBlackListRepository repository) {
        this.repository = repository;
    }

    /***
     * 查询IP地址黑名单
     *
     * @param condition 查询条件
     * @return java.util.List<qc.module.platform.dto.ipBlackList.IpBlackListDto>
     * @author QuCheng Tech
     * @since 2025/1/11
     */
    public List<BlackListDto> query(BlackListQueryConditionDto condition) {
        LambdaQueryWrapper<QcBlackList> wrapper = new LambdaQueryWrapper<>();

        //可以没有查询条件，没有查询条件时默认查询当前有效的黑名单
        if (condition == null || condition.isOnlyvalid() == true) {
            Date now = DateUtil.getNowDate();

            //tm<=now and (expiredtm is null or expiredtm >=now)
            wrapper.lt(QcBlackList::getTm, now);
            wrapper.and(q -> q.isNull(QcBlackList::getExpiredtm).or().gt(QcBlackList::getExpiredtm, now));
        }
        if (condition != null) {
            if (StringUtils.isNotBlank(condition.getIp())) {
                wrapper.like(QcBlackList::getId, condition.getIp());
            }
            if (StringUtils.isNotBlank(condition.getKeywords())) {
                //关键字在URL、原因说明和操作人信息中模糊匹配
                wrapper.and(q -> q.like(QcBlackList::getUrl, condition.getKeywords()).or().like(QcBlackList::getReason, condition.getKeywords()).or().like(QcBlackList::getUsername, condition.getKeywords()));
            }
        }
        //排序按照起始时间降序、过期时间降序
        wrapper.orderByDesc(QcBlackList::getTm).orderByDesc(QcBlackList::getExpiredtm);

        List<QcBlackList> ens = repository.selectList(wrapper);
        if (ens != null && ens.size() > 0x0) {
            List<BlackListDto> result = new ArrayList<>();

            for (QcBlackList en : ens) {
                result.add(BlackListMapper.MAPPER.entityToDto(en));
            }

            return result;
        }

        return null;
    }

    /***
     * 新增Ip地址黑名单，用于controller中调用
     *
     * @param dto 新增对象
     * @param username 操作的用户名称
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2025/1/11
     */
    public String add(BlackListAddDto dto, String username) {
        if (dto == null)
            return "新增的黑名单信息不能为空";
        //IP和URL至少要有1个
        if (StringUtils.isBlank(dto.getIp()) && StringUtils.isBlank(dto.getUrl()))
            return "新增的黑名单IP地址和URL不能同时为空，至少要有一个不为空";
        if (StringUtils.isBlank(dto.getReason()))
            return "新增的黑名单原因说明信息不能为空";

        Date expiredTime = null;

        Date now = DateUtil.getNowDate();

        //判断过期时间如果有，转换是否正确
        if (StringUtils.isNotBlank(dto.getExpiredtm())) {
            expiredTime = DateUtil.parseDate(dto.getExpiredtm());
            if (expiredTime == null)
                return "新增的黑名单中过期时间不是有效的时间";
            if (expiredTime.before(now))
                return "新增的黑名单中过期时间错误，应在当前时间之后";
        }

        add(dto.getIp(), dto.getUrl(), dto.getReason(), expiredTime, username);

        return QCUnifyReturnValue.Success();
    }

    /***
     * 新增IP地址黑名单，传入固定的几个参数
     *
     * @param ip IP地址
     * @param url URL
     * @param reason 加入黑名单的原因说明
     * @param expiredTime 过期时间，可以为null
     * @param username 操作的用户名称，可以为空
     * @return void
     * @author QuCheng Tech
     * @since 2025/1/11
     */
    public void add(String ip, String url, String reason, Date expiredTime, String username) {
        Date now = DateUtil.getNowDate();

        QcBlackList en = new QcBlackList();
        en.setTm(now);
        en.setIp(ip);
        en.setUrl(url);
        en.setReason(reason);
        if (expiredTime != null)
            en.setExpiredtm(expiredTime);
        en.setUsername(username);
        //添加时初始命中次数为0
        en.setHitcount(0x0);

        repository.insert(en);
    }

    /***
     * 设置指定Id的Ip地址黑名单为过期
     *
     * @param id IP地址黑名单ID
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2025/1/11
     */
    public String setExpired(int id) {
        //if (StringUtils.isBlank(id))
        //    return "要设置过期的黑名单ID不能为空";

        QcBlackList en = repository.selectById(id);
        if (en == null)
            return "要设置过期的黑名单数据不存在";

        Date now = DateUtil.getNowDate();
        //判断是否已过期
        if (en.getExpiredtm() != null && en.getExpiredtm().before(now))
            return "要设置过期的黑名单已为过期状态";

        //设置为过期，设置为过期后前端进行刷新可能会在同一秒内完成，设置过期时间为当前时间往前1秒
        en.setExpiredtm(DateUtil.addSeconds(now, -1));

        repository.updateById(en);

        return QCUnifyReturnValue.Success();
    }

    /***
     * 判断是否在黑名单中；同时判断IP和URL，如果黑名单中URL为空表示所有URL的均限制访问
     *
     * @param ip IP地址
     * @param url URL
     * @return boolean
     * @author QuCheng Tech
     * @since 2025/1/11
     */
    public boolean isInBlackList(String ip, String url) {
        if (StringUtils.isNotBlank(ip) || StringUtils.isNotBlank(url)) {
            Date now = DateUtil.getNowDate();
            LambdaQueryWrapper<QcBlackList> wrapper = new LambdaQueryWrapper<>();

            //判断黑名单的时间：tm<now and (expiredTm>now or expiredTm is null)
            wrapper.lt(QcBlackList::getTm, now);
            wrapper.and(q -> q.isNull(QcBlackList::getExpiredtm).or().gt(QcBlackList::getExpiredtm, now));

            //判断黑名单的IP地址和URL；可能存在黑名单中只设置IP、只设置URL或同时设置IP和URL的不同情况
            if (StringUtils.isNotBlank(ip)) {
                if (StringUtils.isNotBlank(url)) {
                    //同时匹配IP和URL；(ip=ip and url=url) or (ip=ip and url is null) or (url=url and ip is null)
                    wrapper.and(q -> q
                            .or(w -> w.eq(QcBlackList::getIp, ip).eq(QcBlackList::getUrl, url))
                            .or(w -> w.eq(QcBlackList::getIp, ip).isNull(QcBlackList::getUrl))
                            .or(w -> w.eq(QcBlackList::getUrl, url).isNull(QcBlackList::getId)));
                } else {
                    //只匹配IP；ip=ip and url is null
                    wrapper.eq(QcBlackList::getIp, ip).isNull(QcBlackList::getUrl);
                }
            } else if (StringUtils.isNotBlank(url)) {
                //只匹配URL;url=url and ip is null
                wrapper.eq(QcBlackList::getUrl, url).isNull(QcBlackList::getIp);
            }
            
            long count = repository.selectCount(wrapper);
            if (count > 0x0)
                return true;
        }

        return false;
    }
}
